<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Using Selenium</title>
</head>

<body>

<h2>Driving Web Behaviour</h2>

<p><a href="http://seleniumhq.org">Selenium</a> is a powerful
open-source framework for automated web testing. From 2.x, it offers two
APIs: the <b>Selenium API (backwards compatible with Selenium 1.x)</b> and the
new <b>WebDriver API</b>.  Both APIs are available from the same 2.x dependencies,
so JBehave Web Selenium module supports both.  When we talk about Selenium,
we usually intend the framework, while if we need to distinguish between the 
APIs we'll specifically refer to the <b>Selenium API</b> and <b>WebDriver API</b>.</p>

<p>Let's see how we can easily use Selenium to run web-based
stories. The objective in creating a DSL is to represent the
business-domain functionality whilst abstracting away the details of the
implementation, in this case the access to the web layer via a specific
testing tool, such as Selenium. The same DSL should be re-usable with
different testing tools.</p>

<p>As usual, examples speak louder than words:</p>

<pre class="brush: bdd">Scenario: User searches for a single step

Given user is on Home page
When user opens Find Steps page
Then Find Steps page is shown
When user searches for "Given a threshold of 10.0"
Then search returns: "Given a threshold of $threshold"
When user views with methods
Then search returns: "Given a threshold of $threshold,
    [org.jbehave.web.examples.trader.steps.TraderSteps.aThreshold(double)]"
And steps instances include: "TraderSteps,StockExchangeSteps"
</pre>

<p>The TraderWebSteps class is just like any other JBehave steps class:</p>
<script type="syntaxhighlighter" class="brush: java">
<![CDATA[   
public class TraderWebSteps {

    private final Pages pages;

    public TraderWebSteps(Pages pages) {
        this.pages = pages;
    }

    @Given("user is on Home page")
    public void userIsOnHomePage(){        
        pages.home().open();        
    }

    @When("user opens Find Steps page")
    public void userClicksOnFindSteps(){        
        pages.findSteps().open();
    }

    @Then("Find Steps page is shown")
    public void findStepsPageIsShown(){
        pages.findSteps().pageIsShown();
    }
    
    @When("user searches for \"$step\"")
    public void userSearchesForSteps(String step){        
        pages.findSteps().find(step);
    }

    @Then("search returns: \"$stepsOrMethods\"")
    public void stepsFound(List<String> stepsOrMethods){   
        pages.findSteps().found(stepsOrMethods);
    }

    @Then("steps instances include: \"$names\"")
    public void stepsInstancesFound(List<String> names){   
        pages.findSteps().found(names);
    }

}
]]>
</script>

<p>The new element here is that we are using <a href="page-objects.html">Page
Objects</a> to abstract the Selenium behaviour behind pages that specify the
user interaction in more meaningful ways.  The page objects is where
the Selenium magic happens. Note that the use of pages have so far
enabled us not to commit to a specific API, either Selenium API or WebDriver API.</p>

<h2>Configuring JBehave to use Selenium</h2>

<p>As always, we configure JBehave to several different ways.  One is to have 
an embeddable runnable class, e.g.the JUnit-runnable TraderWebStories.  This is 
where the choice of API comes into play.</p>

<h3>Using Selenium API</h3>

<script type="syntaxhighlighter" class="brush: java">
<![CDATA[
public class TraderWebStories extends JUnitStories {
  
    private Selenium selenium = SeleniumConfiguration.defaultSelenium();
    private ConditionRunner conditionRunner = SeleniumConfiguration.defaultConditionRunner(selenium);
    private Pages pages = new Pages(selenium, conditionRunner);
    private SeleniumSteps lifecycleSteps = new PerStorySeleniumSteps(selenium);
    private SeleniumContext seleniumContext = new SeleniumContext();

    @Override
    public Configuration configuration() {
        Class<? extends Embeddable> embeddableClass = this.getClass();
        return new SeleniumConfiguration()
            .useSelenium(selenium) 
            .useSeleniumContext(seleniumContext)
            .useStepMonitor(new SeleniumStepMonitor(selenium, seleniumContext, new SilentStepMonitor()))
            .useStoryLoader(new LoadFromClasspath(embeddableClass))
            .useStoryReporterBuilder(new StoryReporterBuilder()
                .withCodeLocation(codeLocationFromClass(embeddableClass))
                .withDefaultFormats()
                .withFormats(CONSOLE, TXT, HTML, XML));
    }

    @Override
    public InjectableStepsFactory stepsFactory() {
        return new InstanceStepsFactory(configuration(),
                new TraderWebSteps(pages),
                lifecycleSteps,
                new SeleniumScreenshotOnFailure(selenium));
    }
    
    @Override
    protected List<String> storyPaths() {
        return new StoryFinder()
                .findPaths(codeLocationFromClass(this.getClass()).getFile(), asList("**/*.story"), null);
    }

}
]]>
</script>

<h3>Using WebDriver API</h3>

<script type="syntaxhighlighter" class="brush: java">
<![CDATA[
public class TraderWebStories extends JUnitStories {
  
    private WebDriverProvider driverProvider = new PropertyWebDriverProvider();
    private WebDriverSteps lifecycleSteps = new PerStoriesWebDriverSteps(driverProvider); // or PerStoryWebDriverSteps(driverProvider)
    private Pages pages = new Pages(driverProvider);
    private SeleniumContext context = new SeleniumContext();
    private ContextView contextView = new LocalFrameContextView().sized(500, 100);
    
    public TraderWebStories() {
        // If configuring lifecycle per-stories, you need to ensure that you a same-thread executor
        if ( lifecycleSteps instanceof PerStoriesWebDriverSteps ){
            configuredEmbedder().useExecutorService(MoreExecutors.sameThreadExecutor());
        }
    }

    @Override
    public Configuration configuration() {
        Class<? extends Embeddable> embeddableClass = this.getClass();
        return new SeleniumConfiguration()
                .useSeleniumContext(context)
                .useWebDriverProvider(driverProvider)
                .useStepMonitor(new SeleniumStepMonitor(contextView, context, new SilentStepMonitor()))
                .useStoryLoader(new LoadFromClasspath(embeddableClass))
                .useStoryReporterBuilder(new StoryReporterBuilder()
                    .withCodeLocation(codeLocationFromClass(embeddableClass))
                    .withDefaultFormats()
                    .withFormats(CONSOLE, TXT, HTML, XML));
    }

    @Override
    public InjectableStepsFactory stepsFactory() {
        Configuration configuration = configuration();
        return new InstanceStepsFactory(configuration, 
                new TraderWebSteps(pages),
                lifecycleSteps,
                new WebDriverScreenshotOnFailure(driverProvider, configuration.storyReporterBuilder()));
    }


    @Override
    protected List<String> storyPaths() {
        return new StoryFinder()
                .findPaths(codeLocationFromClass(this.getClass()).getFile(), asList("**/*.story"), null);
    }

    // This Embedder is used by Maven or Ant and it will override anything set in the constructor
    public static class SameThreadEmbedder extends Embedder {
        
        public SameThreadEmbedder() {
            useExecutorService(MoreExecutors.sameThreadExecutor());
        }

    }

}
]]>
</script>

<h2>Lifecycle Steps</h2>

<p>JBehave integration with Selenium and WebDriver APIs aims to facilitate common tasks.  Amongst these, one of the most common is the management of the 
lifecycle, e.g. starting and stopping the browser.</p>

<p>JBehave provides three type of lifecycle management:</p>
<ol>
<li>per-stories: via the <a
            href="javadoc/web-selenium/org/jbehave/web/selenium/PerStoriesSeleniumSteps.html">PerStoriesSeleniumSteps</a> or 
            <a
            href="javadoc/web-selenium/org/jbehave/web/selenium/PerStoriesWebDriverSteps.html">PerStoriesWebDriverSteps</a>.</li>
<li>per-story: via the <a
            href="javadoc/web-selenium/org/jbehave/web/selenium/PerStorySeleniumSteps.html">PerStorySeleniumSteps</a> or 
            <a
            href="javadoc/web-selenium/org/jbehave/web/selenium/PerStoryWebDriverSteps.html">PerStoryWebDriverSteps</a>.</li>
<li>per-scenario: via the <a
            href="javadoc/web-selenium/org/jbehave/web/selenium/PerScenarioSeleniumSteps.html">PerScenarioSeleniumSteps</a> or 
            <a
            href="javadoc/web-selenium/org/jbehave/web/selenium/PerScenarioWebDriverSteps.html">PerScenarioWebDriverSteps</a>.</li>
</ol>

<span class="followup">If you are using per-stories lifecycle, you must ensure that you are using a same-thread executor.  C.f. the trader-webdriver example on how to configure the 
use of a same-thread executor.</span>

<h2>Running Selenium-based tests in an automated way</h2>

<p><strong>NOTE</strong>: To get the Selenium based tests running in
an automated way, you need to run both a webapp server, e.g. Jetty, and
the Selenium server (only for the Selenium API, as the WebDriver API does not need it). See the <a
	href="https://github.com/jbehave/jbehave-web/blob/master/examples/trader-runner-selenium">trader
runner selenium example</a> and <a
    href="https://github.com/jbehave/jbehave-web/blob/master/examples/trader-runner-webdriver">trader
runner webdriver example</a> for a way to do this using <a
	href="http://maven.apache.org">Maven</a>.</p>

</body>
</html>
